#include "defines.h"

#ifdef ENABLE_ONLINE

import java.io.*;
import javax.microedition.io.*;

public final class HTTP implements Runnable
{
    // Number of bytes to read from response at one time.
    private final int RECEIVEBUFFERSIZE = 16;

    // Pointer to current thread doing transfer, used to wait for previous
    // transfer to end.
    private Thread m_thread = null;

    // Where to send
    private String m_sUrl;

    // Used only with POST requests (HTTPConfig.USE_HTTP_POST == true)
    private String m_sQuery; // what to send

    // Network objects.
    private HttpConnection m_c = null;
    private InputStream m_i = null;

    // Used only with POST requests (HTTPConfig.USE_HTTP_POST == true)
    private OutputStream m_o = null;

    
    //  The received message buffer.
    // Check this after a transaction completes to get the received data.
    public String m_response;

    // The received message buffer in byte array.
    // Check this after a transaction completes to get the received data.
    public byte[] m_responseByteArray;

    // Set to true when in the middle of a http trasaction.
    private boolean m_bInProgress = false;

    // Set to true when the transaction is canceled.
    private boolean m_bCanceled = false;

    // Tells wether an error occured during the HTTP transaction.
    // It is set to true if an error has been encountered and false otherwise.
    public boolean m_bError = false;

    // The User-Agent to be used.
    // If null, no user agent will be set (device defaults will be used instead).
    public static String userAgent = null;

    // Provides information wether this class' instance is currently doing a
    // transfer or not.
    // return true if a call is in progress, false otherwise.
    public boolean isInProgress()
    {
        return m_bInProgress;
    }

    // Tells wether an error occured during the transaction.
    // return true if an error has been encountered, false otherwise.
    public boolean isErrorOccurred()
    {
        return m_bError;
    }


    
    // Cancels the ongoing transaction.
    // It is safe to call at any time, but keep in mind that the request may have
    // already reached the server, triggering any changes it implied. Those
    // changes will not be discarded by the server and will be saved.
    public void cancel()
    {
        m_bCanceled = true;

        if (HTTPConfig.HTTP_NO_CANCEL)
        {
            return;
        }
        else
        {
            if (m_c != null)
            {
                if (HTTPConfig.ENABLE_DEBUG)
                {
                    System.out.println("HTTP: cancel");
                }
                try
                {
                    synchronized (m_c)
                    {
                        m_i.close();
                    }
                }
                catch (Exception e)
                {}
                try
                {
                    synchronized (m_c)
                    {
                        m_c.close();
                    }
                }
                catch (Exception e)
                {}

                if (HTTPConfig.USE_HTTP_POST)
                {
                    try
                    {
                        synchronized (m_o)
                        {
                            m_o.close();
                        }
                    }
                    catch (Exception e)
                    {}

                    m_o = null;
                }
            }

            m_i = null;
            m_c = null;


            m_bInProgress = false;
            m_thread = null;
            // Run the garbage collector.
			System.gc();
        }

    }


    // Frees any resources used by this class.
    // It is OK to call at any time, but it should be called after using any data
    // returned from the server as it also deletes m_response.
    // Side effect:
    // It will cancel the current transaction, if any.
    // see m_response
    public void cleanup()
    {
        cancel();
        m_response = null;
        m_responseByteArray = null;
    }


    // The entry point. It starts a HTTP request.
    // param sUrl URL of the server
    // param sQuery The query to be sent to the server
    public void sendByGet(String sUrl, String sQuery)
    {
        // Run the garbage collector.
		System.gc();
        if (HTTPConfig.ENABLE_DEBUG)
        {
            System.out.println("HTTP: sendByGet:url [" + sUrl + "] [" + sQuery +"] ");
        }
        // blocking wait on previous request end
        while (m_bInProgress)
        {
            try
            {
                /*if (HTTPConfig.ENABLE_TIMEOUT)
                {
                    // check if enough time has passed
                    if (System.currentTimeMillis() - XPlayer.callstarttime >
                            HTTPConfig.CONN_TIMEOUT)
                    {
                        // Clean up connections and sets m_bInProgress to false.
                        cancel();

                        // TODO: Decide if this is necessary.
                        // Reset the starttime so the handle functions wait a bit
                        // for the response before timing out.
                        //XPlayer.callstarttime = System.currentTimeMillis();
                    }
                }*/

                synchronized (this)
                {
                    // This causes the game thread to block for 50 milliseconds
                    // every iteration.
                    wait(50);
                }
            }
            catch (Exception e)
            {}
        }

        //in the middle of transaction
        m_bInProgress = true;
        m_bCanceled = false;

        //store where and what to send
        if (HTTPConfig.USE_HTTP_POST)
        {
            // for http get, we need to store separately the server address and the
            // request body
            m_sUrl = sUrl;
            m_sQuery = sQuery;
        }
        else
        {
            // for http get, the url contains the query.
            m_sUrl = sUrl + sQuery;
        }

        //wait for the previous trasaction
        if (m_thread != null)
        {
            try
            {
                m_thread.join();
            }
            catch (Exception e)
            {}
        }

        //begin a thread which will do the transaction
        //m_timeBegin = System.currentTimeMillis();
        m_bError = false;
        m_thread = new Thread(this);
        m_thread.start();
    }


    // The main thread handling the HTTP request. It is started by the
    // sendByGet(String, String) function.
    // see sendByGet(String, String)
    public void run()
    {
        if (m_sUrl == null)
        {
            if (HTTPConfig.ENABLE_DEBUG)
            {
                System.out.println("HTTP: run: URL is null");
            }
            cancel();
            m_bError = true;
            m_bInProgress = false;
            m_bCanceled = false;
            return;
        }

        if (HTTPConfig.USE_HTTP_POST)
        {// for http post, need to test the query too.
            if (m_sQuery == null)
            {
                if (HTTPConfig.ENABLE_DEBUG)
                {
                    System.out.println("HTTP: run: Query is null");
                }
                cancel();
                m_bError = true;
                m_bInProgress = false;
                m_bCanceled = false;
                return;
            }

            if (HTTPConfig.ENABLE_DEBUG)
            {
                System.out.println("Using HTTP Post - this phone must have problems with long GET REQUESTS.");
            }

        }

        try
        {
            m_bError = false;

            //open connection
            if (HTTPConfig.ENABLE_DEBUG)
            {
                System.out.println("HTTP: run:connecting to [" + m_sUrl + "]");
            }

            if (m_bCanceled)
            {
                m_bInProgress = false;
                return;
            }
            m_c = (HttpConnection) Connector.open(m_sUrl, Connector.READ_WRITE);

            if (m_bCanceled)
            {
                m_bInProgress = false;
                return;
            }

            // set the corresponding HTTP method method
            if (HTTPConfig.USE_HTTP_POST)
            {
                m_c.setRequestMethod(HttpConnection.POST);
            }
            else
            {
                m_c.setRequestMethod(HttpConnection.GET);
            }

            m_c.setRequestProperty("Connection", "close");

            if (userAgent != null)
            {
                m_c.setRequestProperty("User-Agent", userAgent);
            }

            if (HTTPConfig.USE_HTTP_POST)
            {
                m_c.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
                String sQuery = "b=" + m_sQuery;
                m_c.setRequestProperty("Content-Length",String.valueOf(sQuery.length()));

                m_o = m_c.openOutputStream();
                m_o.write(sQuery.getBytes(), 0, sQuery.length());
                m_o.flush();
            }

            if (m_bCanceled)
            {
                m_bInProgress = false;
                return;
            }

            if (HTTPConfig.ENABLE_DEBUG)
            {
                System.out.println("HTTP: run: receive");
            }

            // actually this opens the input connection
            if (m_c.getResponseCode() != m_c.HTTP_OK)
            {
                cancel();
                m_bError = true;
                m_bInProgress = false;
                m_bCanceled = false;
                return;
            }

            if (m_bCanceled)
            {
                m_bInProgress = false;
                return;
            }

            synchronized (m_c)
            {
                m_i = m_c.openInputStream();
            }


            m_thread.yield();
            if (m_bCanceled)
            {
                m_bInProgress = false;
                return;
            }

            ByteArrayOutputStream bao = new ByteArrayOutputStream();

            byte[] abInBuffer = new byte[RECEIVEBUFFERSIZE];
            int nBytesRead = 0;
            int i;
            while (nBytesRead != -1)
            {
                if (m_bCanceled)
                {
                    m_bInProgress = false;
                    return;
                }

                /********************** SANYO PHONE SPECIFIC **********************
                 * sanyo phones for some reason always return result the same as
                 * RECEIVEBUFFERSIZE even when it actually read less so need to
                 * reset buffer to all 0 before read, then cut off the 0's for it
                 * to work properly
                 ******************************************************************/
                if (HTTPConfig.USE_BUG_FIX_MESSAGE_SIZE)
                {
                    for (i = 0; i < RECEIVEBUFFERSIZE; i++)
                    {
                        abInBuffer[i] = 0;
                    }
                }
                /******************** END SANYO PHONE SPECIFIC ********************/

                nBytesRead = m_i.read(abInBuffer, 0, RECEIVEBUFFERSIZE);

                if (m_bCanceled)
                {
                    m_bInProgress = false;
                    return;
                }

                if (nBytesRead == -1)
                {
                    break;
                }

                // note that I am checking backwards, this is required so this code
                // can work with current JCP server which sometimes returns a 0 at
                // the begining of response (non-http standard, hope it will be
                // fixed soon)
                if (HTTPConfig.USE_BUG_FIX_MESSAGE_SIZE)
                {
                    for (i = RECEIVEBUFFERSIZE - 1; i >= 0; i--)
                    {
                        if (abInBuffer[i] != 0)
                        {
                            break;
                        }
                    }
                    /*if(i<RECEIVEBUFFERSIZE)*/
                    nBytesRead = i + 1;
                }
                /******************** END SANYO PHONE SPECIFIC ********************/

                if (HTTPConfig.ENABLE_DEBUG)
                {
                    System.out.println("HTTP: run: read [" + nBytesRead + "] bytes from server");
                }
                bao.write(abInBuffer, 0, nBytesRead);
            }

            if (HTTPConfig.ENABLE_DEBUG)
            {
                System.out.println("HTTP: run: received [" + bao.toString() + "]");
            }
            if (HTTPConfig.ENABLE_DEBUG)
            {
                System.out.println("HTTP: run: total bytes read: [" + bao.size() + "]");
            }

            if (m_bCanceled)
            {
                m_bInProgress = false;
                return;
            }

            m_response = bao.toString();
            m_responseByteArray = bao.toByteArray();
        }
        catch (Exception e)
        {
            if (HTTPConfig.ENABLE_DEBUG)
            {
                System.out.println("HTTP: run: exception : " + e.toString());
            }

            m_bError = true;
            m_bInProgress = false;
            return;
        }
        finally
        {
            if (HTTPConfig.ENABLE_DEBUG)
            {
                System.out.println("HTTP: run: finally");
            }

            //clean up
            cancel();

            m_thread = null;
            // Run the garbage collector.
			System.gc();
            //done
            m_bInProgress = false;
        }
    }
}

#endif